#include "BMD_136.h"

#include	<stdio.h>
//#include <gl/glaux.h>

GLuint		texture[14];
GameController gc;
float backR, backG, backB;
const int defR = 30;
const int defG = 70;
const int defB = 0;

static const int texBOMB = 0;
static const int texBOMBER_N = 1;
static const int texBOMBER_E = 2;
static const int texBOMBER_S = 3;
static const int texBOMBER_W = 4;
static const int texBLOCK = 5;
static const int texBRICK = 6;
static const int texEXPLOSION = 7;
static const int texEXPLOSION_BRICK = 8;
static const int texPupBOMB = 9;
static const int texPupSPEED = 10;
static const int texPupPOWER = 11;
static const int texPupKICK = 12;
static const int texPupDETO = 13;


//OpenGL canvas coordinates start from bottom left.
//bmdEngine coordinates start from top left
//must be careful when using offsets with this
double flip(double x){return -x;}

void draw_texture(const double ti, const double tj, const double dH, const double dV)
{
	glTexCoord2f(1.0, 1.0);
	glVertex2d(ti + dH, flip(tj));	// Top Right
	glTexCoord2f(0.0, 1.0);
	glVertex2d(ti, flip(tj));	// Top Left
	glTexCoord2f(0.0, 0.0);
	glVertex2d(ti, flip(tj) - dV);	// Bottom Left
	glTexCoord2f(1.0, 0.0);	
	glVertex2d(ti + dH, flip(tj) - dV);	// Bottom Right
}

GLuint loadTexture(const char* textName)	
{
	/////////////////////////////////////////////
	// NEW! - This function has been completely 
	// rewritten to use FreeImage.
	/////////////////////////////////////////////

    //const char textName[64] = ".\\woodfloor.tga";

	// Get the image file type from FreeImage.
	FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType(textName, 0);

	// Actually load the image file.
	FIBITMAP *dib = FreeImage_Load(fifmt, textName,0);

	// Now, there is no guarantee that the image file
	// loaded will be GL_RGB, so we force FreeImage to
	// convert the image to GL_RGB.
    dib = FreeImage_ConvertTo24Bits(dib);
	

    if( dib != NULL )
	{
		GLuint textureID;
        glGenTextures( 1, &textureID );
		glBindTexture( GL_TEXTURE_2D, textureID );
		glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );

		
		// This is important to note, FreeImage loads textures in
		// BGR format. Now we could just use the GL_BGR extension
		// But, we will simply swap the B and R components ourselves.
		// Firstly, allocate the new bit data doe the image.
		BYTE *bits = new BYTE[FreeImage_GetWidth(dib) * FreeImage_GetHeight(dib) * 3];

		// get a pointer to FreeImage's data.
		BYTE *pixels = (BYTE*)FreeImage_GetBits(dib);

		// Iterate through the pixels, copying the data
		// from 'pixels' to 'bits' except in RGB format.
		for(unsigned int pix=0; pix<FreeImage_GetWidth(dib) * FreeImage_GetHeight(dib); pix++)
		{
			bits[pix*3+0]=pixels[pix*3+2];
			bits[pix*3+1]=pixels[pix*3+1];
			bits[pix*3+2]=pixels[pix*3+0];

		}
	     
        // The new 'glTexImage2D' function, the prime difference
		// being that it gets the width, height and pixel information
		// from 'bits', which is the RGB pixel data..
		glTexImage2D( GL_TEXTURE_2D, 0, 3, FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), 0,
				GL_RGB, GL_UNSIGNED_BYTE, bits );

		
		// Unload the image.
		// and free the bit data.
		FreeImage_Unload(dib);
		delete bits;

		return textureID;
	}
	return 0;
}


void LoadTextures()
{
	texture[texBOMB] = loadTexture("bmdBomb.bmp");
	texture[texBOMBER_N] = loadTexture("bmdBomberN.bmp");
	texture[texBOMBER_E] = loadTexture("bmdBomberE.bmp");
	texture[texBOMBER_S] = loadTexture("bmdBomberS.bmp");
	texture[texBOMBER_W] = loadTexture("bmdBomberW.bmp");
	texture[texBLOCK] = loadTexture("bmdBlock.bmp");
	texture[texBRICK] = loadTexture("bmdBrick.bmp");
	texture[texEXPLOSION] = loadTexture("bmdExplosion.bmp");
	texture[texEXPLOSION_BRICK] = loadTexture("bmdExplosionBrick.bmp");
	texture[texPupBOMB] = loadTexture("bmdPbomb.bmp");
	texture[texPupSPEED] = loadTexture("bmdPspeed.bmp");
	texture[texPupPOWER] = loadTexture("bmdPpower.bmp");
	texture[texPupKICK] = loadTexture("bmdPkick.bmp");
	texture[texPupDETO] = loadTexture("bmdPdeto.bmp");
}


bmdGLCanvas::bmdGLCanvas(wxFrame* parent, int* args) :
wxGLCanvas(parent, wxID_ANY,  wxPoint(0,0), wxSize(640,480), 0, wxT("GLCanvas"),  args)
{
	initialized = false;
	m_gllist = 0;
	Connect( wxID_ANY, wxEVT_PAINT,
		(wxObjectEventFunction) &bmdGLCanvas::OnPaint );
	Connect( wxID_ANY, wxEVT_ERASE_BACKGROUND,
		(wxObjectEventFunction) &bmdGLCanvas::OnEraseBackground );
	Connect( wxID_ANY, wxEVT_SIZE,
		(wxObjectEventFunction) &bmdGLCanvas::OnSize); 

}

void bmdGLCanvas::OnSize(wxSizeEvent& event)
{
    // this is also necessary to update the context on some platforms
    wxGLCanvas::OnSize(event);
    //ResetProjectionMode();
}

void bmdGLCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
{
    // nothing - apparently prevents flicker on Windows
}

void bmdGLCanvas::InitGL()
{
	SetCurrent();

	glShadeModel(GL_SMOOTH);						// Enable Smooth Shading
	glClearColor(backR, backG, backB, 0.5f);					// Black Background
	glClearDepth(1.0f);							// Depth Buffer Setup
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);					// Set Line Antialiasing
	glEnable(GL_BLEND);							// Enable Blending
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);			// Type Of Blending To Use
	LoadTextures();

}

void bmdGLCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
{
	wxPaintDC dc(this); //program freezes if this is not used

    //this->SetCurrent();
	SetCurrent();

	if(!initialized)
	{
		InitGL();
		initialized = true;
	}



    /* clear color and depth buffers */
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glClearColor (backR, backG, backB, 0.5f);

	glLineWidth(1.0f);
	//glDisable(GL_LINE_SMOOTH);
	glLoadIdentity();

	float resH = 640.0, resV = 480.0;
	unsigned int nHmin = 0, nVmin = 0; 
	unsigned int nHmax = 24, nVmax = 18; // < 48
	double dH, dV;

	nHmax = gc.engine.getWidth();
	nVmax = gc.engine.getHeight();

	if((nHmax - nHmin)*resV >= (nVmax - nVmin)*resH)
	{
		dH = 2.0/ (nHmax - nHmin);
		dV = dH * resH / resV;
	}
	else
	{
		dV = 2.0 / (nVmax - nVmin);
		dH = dV * resV / resH;
	}

	/*
	glColor3f(1.0f,0.0f,0.0f);
	glBegin(GL_LINES);
	for(unsigned int i = nHmin; i < nHmax + 1; i++) //+1 so a true border is shown
	{
			glVertex2d(i*dH - 1.0, -1.0);
			glVertex2d(i*dH - 1.0, 1.0);
	}

	//glColor3f(0.0f,1.0f,0.0f);
	for(unsigned int i = nVmin; i < nVmax + 1; i++) //+1 so a true border is shown
	{
			glVertex2d(-1.0 , flip(i*dV - 1.0));
			glVertex2d(1.0 , flip(i*dV - 1.0));
	}
	glEnd();
	*/

	//glEnable(GL_ALPHA_TEST);
	//glAlphaFunc(GL_GREATER,0.1f);

	


	double ti, tj;
	int temp_d;
	vector<PowerUp>::iterator powerup;
	
	glEnable(GL_TEXTURE_2D);				// Enable Texture Mapping

	glColor3f(1.0f,1.0f,1.0f);				// Bright White Color


	glBindTexture(GL_TEXTURE_2D, texture[texPupBOMB]);		// select Pbomb (powerup) image
	glBegin(GL_QUADS);			// Draw A Textured Quad
	powerup = gc.engine.powerups.begin();
	for(; powerup != gc.engine.powerups.end(); powerup++)
	{
		if(powerup->power_type_index == pBOMBS)
		{
			ti = (powerup->coords.x)*dH - 1.0;
			tj = (powerup->coords.y)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
	}
	glEnd();				// Done Texturing The Box

	glBindTexture(GL_TEXTURE_2D, texture[texPupSPEED]);		// select Pspeed (powerup) image
	glBegin(GL_QUADS);			// Draw A Textured Quad
	powerup = gc.engine.powerups.begin();
	for(; powerup != gc.engine.powerups.end(); powerup++)
	{
		if(powerup->power_type_index == pSPEED)
		{
			ti = (powerup->coords.x)*dH - 1.0;
			tj = (powerup->coords.y)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
	}
	glEnd();				// Done Texturing The Box

	glBindTexture(GL_TEXTURE_2D, texture[texPupPOWER]);		// select Ppower (powerup) image
	glBegin(GL_QUADS);			// Draw A Textured Quad
	powerup = gc.engine.powerups.begin();
	for(; powerup != gc.engine.powerups.end(); powerup++)
	{
		if(powerup->power_type_index == pPOWER)
		{
			ti = (powerup->coords.x)*dH - 1.0;
			tj = (powerup->coords.y)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
	}
	glEnd();				// Done Texturing The Box

	glBindTexture(GL_TEXTURE_2D, texture[texPupKICK]);		// select Pkicking (powerup) image
	glBegin(GL_QUADS);			// Draw A Textured Quad
	powerup = gc.engine.powerups.begin();
	for(; powerup != gc.engine.powerups.end(); powerup++)
	{
		if(powerup->power_type_index == pKICKING)
		{
			ti = (powerup->coords.x)*dH - 1.0;
			tj = (powerup->coords.y)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
	}
	glEnd();				// Done Texturing The Box

	glBindTexture(GL_TEXTURE_2D, texture[texPupDETO]);		// select Pdeto (powerup) image
	glBegin(GL_QUADS);			// Draw A Textured Quad
	powerup = gc.engine.powerups.begin();
	for(; powerup != gc.engine.powerups.end(); powerup++)
	{
		if(powerup->power_type_index == pDETONATION)
		{
			ti = (powerup->coords.x)*dH - 1.0;
			tj = (powerup->coords.y)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
	}
	glEnd();				// Done Texturing The Box









	glBindTexture(GL_TEXTURE_2D, texture[texBOMB]);		// select bomb image
	glBegin(GL_QUADS);			// Draw A Textured Quad
	vector<Bomb>::iterator bomb = gc.engine.bombs.begin();
	for (; bomb != gc.engine.bombs.end(); bomb++)
	{
		ti = (bomb->coords.x + bomb->mstate.move_fracX())*dH - 1.0;
		tj = (bomb->coords.y + bomb->mstate.move_fracY())*dV - 1.0;
		draw_texture(ti,tj,dH,dV);
	}
	glEnd();				// Done Texturing The Box


	//glBindTexture(GL_TEXTURE_2D, texture[texBOMBER_N]);		// select bomber image

	vector<Bomber>::iterator bomber = gc.engine.bombers.begin();
	for(; bomber != gc.engine.bombers.end(); bomber++)
	{
		if(bomber->is_alive())
		{
			temp_d = bomber->mstate.get_direction();
			if(temp_d == dNORTH)
				glBindTexture(GL_TEXTURE_2D, texture[texBOMBER_N]);
			else if(temp_d == dEAST)
				glBindTexture(GL_TEXTURE_2D, texture[texBOMBER_E]);
			else if(temp_d == dWEST)
				glBindTexture(GL_TEXTURE_2D, texture[texBOMBER_W]);
			else //dSOUTH or dVOID both face south
				glBindTexture(GL_TEXTURE_2D, texture[texBOMBER_S]);

			glBegin(GL_QUADS);			// Draw A Textured Quad

			ti = (bomber->coords.x + bomber->mstate.move_fracX())*dH - 1.0;
			tj = (bomber->coords.y + bomber->mstate.move_fracY())*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
			glEnd();				// Done Texturing The Box
		}
	}

	
					// Disable Texture Mapping


	glBindTexture(GL_TEXTURE_2D, texture[texBLOCK]);		// select block image
	glBegin(GL_QUADS);			// Draw A Textured Quad

	for(unsigned int i = nHmin; i < nHmax; i++)
		for(unsigned int j = nVmin; j < nVmax; j++)
		{
			if(gc.engine.check_tile_index(i,j,2)) //2 is the tile index for blocks
			{
				ti = i*dH - 1.0;
				tj = j*dV - 1.0;
				draw_texture(ti,tj,dH,dV);
			}
		}
	glEnd();
	//glDisable(GL_TEXTURE_2D);				// Disable Texture Mapping

	glBindTexture(GL_TEXTURE_2D, texture[texBRICK]);		// select brick image
	glBegin(GL_QUADS);			// Draw A Textured Quad

	for(unsigned int i = nHmin; i < nHmax; i++)
		for(unsigned int j = nVmin; j < nVmax; j++)
		{
			if(gc.engine.check_tile_index(i,j,1)) //1 is the tile index for blocks
			{
				ti = i*dH - 1.0;
				tj = j*dV - 1.0;
				draw_texture(ti,tj,dH,dV);
			}
		}
	glEnd();


	glBindTexture(GL_TEXTURE_2D, texture[texEXPLOSION_BRICK]);		// select explosion brick image
	glBegin(GL_QUADS);			// Draw A Textured Quad

	for(unsigned int i = nHmin; i < nHmax; i++)
		for(unsigned int j = nVmin; j < nVmax; j++)
		{
			if(gc.engine.check_tile_index(i,j,tEXPLOSION_BRICK)) //1 is the tile index for blocks
			{
				ti = i*dH - 1.0;
				tj = j*dV - 1.0;
				draw_texture(ti,tj,dH,dV);
			}
		}
	glEnd();


	glBindTexture(GL_TEXTURE_2D, texture[texEXPLOSION]);		// select explosion image
	glBegin(GL_QUADS);			// Draw A Textured Quad

	vector<Explosion>::iterator explosion = gc.engine.explosions.begin();
	for(; explosion != gc.engine.explosions.end(); explosion++)
	{
		ti = explosion->explosion_data.center.x*dH - 1.0;
		tj = explosion->explosion_data.center.y*dV - 1.0;
		draw_texture(ti,tj,dH,dV);


		for(unsigned int i = 1; i <= explosion->explosion_data.dW; i++)
		{
			ti = (explosion->explosion_data.center.x - i)*dH - 1.0;
			tj = explosion->explosion_data.center.y*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
		for(unsigned int i = 1; i <= explosion->explosion_data.dE; i++)
		{
			ti = (explosion->explosion_data.center.x + i)*dH - 1.0;
			tj = explosion->explosion_data.center.y*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
		for(unsigned int i = 1; i <= explosion->explosion_data.dN; i++)
		{
			ti = explosion->explosion_data.center.x*dH - 1.0;
			tj = (explosion->explosion_data.center.y - i)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
		for(unsigned int i = 1; i <= explosion->explosion_data.dS; i++)
		{
			ti = explosion->explosion_data.center.x*dH - 1.0;
			tj = (explosion->explosion_data.center.y + i)*dV - 1.0;
			draw_texture(ti,tj,dH,dV);
		}
	}
	glEnd();




	glDisable(GL_TEXTURE_2D);				// Disable Texture Mapping


    glFlush();
    SwapBuffers();
}





const int bID_QUIT = 1;
const int bID_STEP = 2;
const int bID_LEFT = 3;
const int bID_RIGHT = 4;
const int bID_UP = 5;
const int bID_DOWN = 6;
const int bID_PLACE = 7;
const int bID_DETO = 8;
const int bID_TCONTROL = 9;
const int bID_FASTER = 10;
const int bID_SLOWER = 11;

MasterFrame::MasterFrame(const wxString& title)
       : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(780, 480))
{
	gc.test_init();
	
	//(defR, defG, defB);
	wxColor temp_color(defR,defG,defB);
	this->SetBackgroundColour(temp_color);

	//we can get his from defR,defB,defG
	//however if the change is rejected for any reason
	//this will make it look slightly better
	temp_color = this->GetBackgroundColour();
	backR = (float)temp_color.Red() / 255.0;
	backG = (float)temp_color.Green() / 255.0;
	backB = (float)temp_color.Blue() / 255.0;

	int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0};
	gl_canvas = new bmdGLCanvas(this, args);
	gl_canvas->Show();

	quit_button = new wxButton(this, bID_QUIT, wxString("Quit"),wxPoint(660,30));
	quit_button->Show();

	step_button = new wxButton(this, bID_STEP, wxString("Step"),wxPoint(660, 60));
	step_button->Show();

	tcontrol_button = new wxButton(this, bID_TCONTROL, wxString("START"),wxPoint(660, 300));
	tcontrol_button->Show();
	faster_button = new wxButton(this, bID_FASTER, wxString("Step Faster"),wxPoint(660, 330));
	faster_button->Show();

	slower_button = new wxButton(this, bID_SLOWER, wxString("Step Slower"),wxPoint(660, 360));
	slower_button->Show();

	//gl_canvas->InitGL();
	//gl_canvas->OnPaint();

	Connect(bID_QUIT, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MasterFrame::OnQuit));
	Connect(bID_STEP, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MasterFrame::OnStep));
	Connect(bID_TCONTROL, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MasterFrame::OnTControl));
	Connect(bID_FASTER, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MasterFrame::OnFaster));
	Connect(bID_SLOWER, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MasterFrame::OnSlower));

	timer = new wxTimer();
	timer->SetOwner(this);
	Connect(wxEVT_TIMER, wxTimerEventHandler(MasterFrame::OnTTick));

	Centre();
}

void MasterFrame::OnTTick(wxTimerEvent& WXUNUSED(event))
{
	gc.run_step();
	gl_canvas->Refresh();

	gc.engine.bombers[0].orders.bomb_place = false;
	gc.engine.bombers[0].orders.detonate = false;
}

void MasterFrame::OnTControl(wxCommandEvent& WXUNUSED(event))
{
	if(timer->IsRunning())
	{
		timer->Stop();
		tcontrol_button->SetLabel(wxString("Continue"));
	}
	else
	{
		timer->Start(TIMER_DELAY);
		tcontrol_button->SetLabel(wxString("Pause"));
	}
}
void MasterFrame::OnFaster(wxCommandEvent& WXUNUSED(event))
{
	if(TIMER_DELAY > 1)
		TIMER_DELAY /= 2;
	
	if(timer->IsRunning())
	{
		timer->Stop();
		timer->Start(TIMER_DELAY);
	}	
}

void MasterFrame::OnSlower(wxCommandEvent& WXUNUSED(event))
{
	if(TIMER_DELAY > 1)
		TIMER_DELAY *= 2;
	
	if(timer->IsRunning())
	{
		timer->Stop();
		timer->Start(TIMER_DELAY);
	}
}




void MasterFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{Close(true);}

void MasterFrame::OnStep(wxCommandEvent& WXUNUSED(event))
{
	gc.run_step();
	gl_canvas->Refresh();}

/*
BEGIN_EVENT_TABLE(MasterFrame, wxFrame)
    EVT_BUTTON(wxID_QUIT,  MasterFrame::OnQuit)
END_EVENT_TABLE()
*/
